package com.tools.common.io;

import com.tools.common.exception.IORuntimeException;
import com.tools.common.object.Force;
import com.tools.common.object.Note;
import com.tools.common.object.string.StrKit;
import com.tools.common.security.SysKit;
import org.apache.poi.ss.usermodel.Workbook;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Base64;

/**
 * 获取 IO 流或
 * 使用 IO 流操作
 * 的工具类
 * */
@Note("获取 IO 流或使用 IO 流操作的工具类")
public final class IOKit extends IOStreamKit {

    private IOKit() {}


    /* *******************************************************************************************
     *
     *          读取操作
     *
     * *******************************************************************************************
     * */

    public static String readToString(String filePath) {
        return readToString(fileReader(filePath), DEFAULT_BUFFERED_BYTE_SIZE, null);
    }

    public static String readToString(String filePath, int bufferSize) {
        return readToString(fileReader(filePath), bufferSize, null);
    }

    public static String readToString(String filePath, Force<String> forceHandle) {
        return readToString(fileReader(filePath), DEFAULT_BUFFERED_BYTE_SIZE, forceHandle);
    }

    public static String readToString(String filePath, int bufferSize, Force<String> forceHandle) {
        return readToString(fileReader(filePath), bufferSize, forceHandle);
    }

    public static String readToString(File file) {
        return readToString(fileReader(file), DEFAULT_BUFFERED_BYTE_SIZE, null);
    }

    public static String readToString(File file, int bufferSize) {
        return readToString(fileReader(file), bufferSize, null);
    }

    public static String readToString(File file, Force<String> forceHandle) {
        return readToString(fileReader(file), DEFAULT_BUFFERED_BYTE_SIZE, forceHandle);
    }

    public static String readToString(File file, int bufferSize, Force<String> forceHandle) {
        return readToString(fileReader(file), bufferSize, forceHandle);
    }

    public static String readToString(Reader reader) {
        return readToString(reader, DEFAULT_BUFFERED_BYTE_SIZE, null);
    }

    public static String readToString(Reader reader, int bufferSize) {
        return readToString(reader, bufferSize, null);
    }

    public static String readToString(Reader reader, Force<String> forceHandle) {
        return readToString(reader, DEFAULT_BUFFERED_BYTE_SIZE, forceHandle);
    }

    @Note("读取字符输入流的内容，转为字符串返回后再经过 forceHandle.force(String) 自定义处理后再拼接组合返回。" +
            "bufferSize 为缓冲区的大小，单位是字符。")
    public static String readToString(Reader reader, int bufferSize, Force<String> forceHandle) {
        if(reader == null) return "";
        if(reader instanceof BufferedReader) return private_bufferedReadOperation((BufferedReader) reader, forceHandle);
        int len;
        char[] chars = new char[bufferSize < 1 ? DEFAULT_BUFFERED_BYTE_SIZE : bufferSize];
        StringBuilder builder = new StringBuilder();
        try{
            if(forceHandle == null) {
                while ((len = reader.read(chars)) != -1) {
                    builder.append(new String(chars, 0, len));
                }
                return builder.toString();
            }
            while ((len = reader.read(chars)) != -1) {
                String handled = forceHandle.force(new String(chars, 0, len));
                builder.append(handled);
            }
        } catch (Exception e) {
            throw new IORuntimeException(e);
        } finally {
            close(reader);
        }
        return builder.toString();
    }

    public static String readToString(InputStream input) {
        return readToString(input, DEFAULT_BUFFERED_BYTE_SIZE);
    }

    public static String readToString(InputStream input, int bufferSize) {
        if(input == null) return "";
        return readToString(input, bufferSize, null);
    }

    public static String readToString(InputStream input, Force<String> forceHandle) {
        if(input == null) return "";
        return readToString(input, DEFAULT_BUFFERED_BYTE_SIZE, forceHandle);
    }

    @Note("读取字节输入流的内容，转为字符串返回后再经过 forceHandle.force(String) 自定义处理后再拼接组合返回。" +
            "bufferSize 为缓冲区的大小，单位为字节。")
    public static String readToString(InputStream input, int bufferSize, Force<String> forceHandle) {
        if(input == null) return "";
        byte[] buffs = new byte[bufferSize < 1 ? DEFAULT_BUFFERED_BYTE_SIZE : bufferSize];
        int length;
        StringBuilder builder = new StringBuilder();
        try {
            if(forceHandle != null) {
                while ((length = input.read(buffs)) != -1) {
                    String handled = forceHandle.force(new String(buffs, 0, length));
                    builder.append(handled);
                }
                return builder.toString();
            }
            while ((length = input.read(buffs)) != -1) {
                builder.append(new String(buffs, 0, length));
            }
            return builder.toString();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(input);
        }
    }


    @Note("读取图片转为 Base64 编码后的字符串")
    public static String readToString(BufferedImage image, String imageSuffix) {
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(image, imageSuffix, byteArrayOutputStream);
            return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(byteArrayOutputStream);
        }
    }


    @Note("图片转为 Base64 编码后的字符串，为了适应前端的 <img> 解析")
    public static String readToBase64ForImg(BufferedImage image, String imageSuffix) {
        return "data:image/" + imageSuffix + ";base64," + readToString(image, imageSuffix);
    }


    public static String readToStringByNIO(String filePath) {
        return readToStringByNIO(filePath, DEFAULT_BUFFERED_BYTE_SIZE);
    }

    public static String readToStringByNIO(String filePath, Force<String> forceHandle) {
        return readToStringByNIO(filePath, DEFAULT_BUFFERED_BYTE_SIZE, forceHandle);
    }

    public static String readToStringByNIO(String filePath, int bufferSize) {
        if(filePath == null || filePath.isEmpty()) return "";
        return readToStringByNIO(fileInputChannel(filePath), bufferSize, null);
    }

    public static String readToStringByNIO(String filePath, int bufferSize, Force<String> forceHandle) {
        if(filePath == null || filePath.isEmpty()) return "";
        return readToStringByNIO(fileInputChannel(filePath), bufferSize, forceHandle);
    }

    public static String readToStringByNIO(File file) {
        return readToStringByNIO(file, DEFAULT_BUFFERED_BYTE_SIZE);
    }

    public static String readToStringByNIO(File file, int bufferSize) {
        if(file == null) return "";
        return readToStringByNIO(fileInputChannel(file), bufferSize, null);
    }

    public static String readToStringByNIO(File file, Force<String> forceHandle) {
        return readToStringByNIO(file, DEFAULT_BUFFERED_BYTE_SIZE, forceHandle);
    }

    public static String readToStringByNIO(File file, int bufferSize, Force<String> forceHandle) {
        if(file == null) return "";
        return readToStringByNIO(fileInputChannel(file), bufferSize, forceHandle);
    }

    public static String readToStringByNIO(FileInputStream fileInput) {
        return readToStringByNIO(fileInput, DEFAULT_BUFFERED_BYTE_SIZE);
    }

    public static String readToStringByNIO(FileInputStream fileInput, int bufferSize) {
        if(fileInput == null) return "";
        FileChannel fileChannel = fileInput.getChannel();
        close(fileInput);
        return readToStringByNIO(fileChannel, bufferSize, null);
    }

    public static String readToStringByNIO(FileInputStream fileInput, Force<String> forceHandle) {
        return readToStringByNIO(fileInput, DEFAULT_BUFFERED_BYTE_SIZE, forceHandle);
    }

    public static String readToStringByNIO(FileInputStream fileInput, int bufferSize, Force<String> forceHandle) {
        if(fileInput == null) return "";
        FileChannel fileChannel = fileInput.getChannel();
        close(fileInput);
        return readToStringByNIO(fileChannel, bufferSize, forceHandle);
    }

    public static String readToStringByNIO(FileChannel fileChannel) {
        return readToStringByNIO(fileChannel, DEFAULT_BUFFERED_BYTE_SIZE, null);
    }

    public static String readToStringByNIO(FileChannel fileChannel, int bufferSize) {
        return readToStringByNIO(fileChannel, bufferSize, null);
    }

    public static String readToStringByNIO(FileChannel fileChannel, Force<String> forceHandle) {
        return readToStringByNIO(fileChannel, DEFAULT_BUFFERED_BYTE_SIZE, forceHandle);
    }


    @Note("使用 NIO 方式读取字节输入流的内容，转为字符串后，再经过 forceHandle.force(String) 自定义处理后再拼接组合返回。" +
            "bufferSize 为缓冲区的大小，单位为字节。")
    public static String readToStringByNIO(FileChannel fileChannel, int bufferSize, Force<String> forceHandle) {
        if(fileChannel == null) return "";
        ByteBuffer buffer = ByteBuffer.allocate(bufferSize < 1 ? DEFAULT_BUFFERED_BYTE_SIZE : bufferSize);
        try {
            StringBuilder builder = new StringBuilder();
            if(forceHandle == null) {
                while (fileChannel.read(buffer) != -1) {
                    buffer.flip();
                    builder.append(new String(buffer.array()));
                    buffer.clear();
                }
            }
            else {
                while (fileChannel.read(buffer) != -1) {
                    buffer.flip();
                    builder.append(forceHandle.force(new String(buffer.array())));
                    buffer.clear();
                }
            }
            return builder.toString();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(fileChannel);
        }
    }



    @Note("将字节流中的二进制转为 BufferedImage 图片实例输出")
    public static BufferedImage readImage(InputStream image) {
        try {
            return ImageIO.read(image);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("将图片文件实例转为 BufferedImage 图片实例输出")
    public static BufferedImage readImage(File image) {
        try {
            return ImageIO.read(image);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("将网络中指定路径的图片文件实例转为 BufferedImage 图片实例输出")
    public static BufferedImage readImage(URL imageUrl) {
        try {
            return ImageIO.read(imageUrl);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    @Note("将指定路径中的图片文件实例转为 BufferedImage 图片实例输出")
    public static BufferedImage readImage(String imagePath) {
        return readImage(fileInputStream(imagePath));
    }


    @Note("将图片文件转为指定图片类型的字节流数组")
    public static byte[] imageToByteArray(BufferedImage image, String imgType) {
        try {
            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
            ImageIO.write(image, imgType, byteOut);
            return byteOut.toByteArray();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    /* *******************************************************************************************
     *
     *          写入操作
     *
     * *******************************************************************************************
     * */

    @Note("将 Excel 工作簿写入到指定的字节输出流中")
    public static void writeWorkbook(Workbook workbook, OutputStream output) {
        try {
            workbook.write(output);
        } catch (Exception e) {
            throw new IORuntimeException(e);
        } finally {
            close(workbook);
            close(output);
        }
    }

    @Note("将字符串写入字符输出流中输出")
    public static void writeFromString(Writer writer, String content) {
        if(writer == null || content == null || content.isEmpty()) return;
        try {
            writer.write(content);
            writer.flush();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(writer);
        }
    }

    @Note("默认把字符串内容按默认的字符集编码转成字节数组，然后覆盖式地写入指定的文件中")
    public static void writeFromString(File outFile, String content) {
        writeFromString(fileWriter(outFile, false), content);
    }

    @Note("默认把字符串内容按指定的字符集编码转成字节数组，然后根据 append 决定是否以追加的形式写入指定的文件中")
    public static void writeFromString(File outFile, boolean append, String content) {
        writeFromString(fileWriter(outFile, append), content);
    }

    @Note("默认把字符串内容按默认的字符集编码转成字节数组，然后覆盖式地写入指定路径的文件中")
    public static void writeFromString(String outFilePath, String content) {
        writeFromString(fileWriter(outFilePath, false), content);
    }

    @Note("默认把字符串内容按指定的字符集编码转成字节数组，然后根据 append 决定是否以追加的形式写入指定路径的文件中")
    public static void writeFromString(String outFilePath, boolean append, String content) {
        writeFromString(fileWriter(outFilePath, append), content);
    }

    @Note("默认把字符串内容按默认的字符集编码转成字节数组，然后写入指定的位置")
    public static void writeFromString(OutputStream output, String content) {
        if(content == null || content.isEmpty()) return;
        private_outPutStreamWriteString(output, content.getBytes());
    }

    @Note("把字符串内容按指定的字符集编码转成字节数组，然后写入指定的位置")
    public static void writeFromString(OutputStream output, String content, CharsetEnum charset) {
        if(content == null || content.isEmpty()) return;
        if(charset == null) throw new IORuntimeException("字符集编码枚举为 null，为了防止解码时乱码，请传入正确的字符集编码枚举");
        private_outPutStreamWriteString(output, content.getBytes(charset.getCharset()));
    }

    @Note("把字符串内容按默认的字符集编码转成字节数组，然后用 NIO 的方式写入指定的位置")
    public static void writeFromString(FileChannel outputChannel, String content) {
        if(outputChannel == null || content == null || content.isEmpty()) return;
        private_nioWriteFromString(outputChannel, content.getBytes());
    }

    @Note("把字符串内容按指定的字符集编码转成字节数组，然后用 NIO 的方式写入指定的位置")
    public static void writeFromString(FileChannel outputChannel, CharsetEnum charset, String content) {
        if(outputChannel == null || content == null || content.isEmpty()) return;
        if(charset == null) throw new NullPointerException("字符集编码枚举为 null，为了防止解码时乱码，请传入正确的字符集编码枚举");
        private_nioWriteFromString(outputChannel, content.getBytes(charset.getCharset()));
    }

    public static void writeFromStringByNIO(FileOutputStream output, String content) {
        FileChannel fileChannel = output.getChannel();
        close(output);
        writeFromString(fileChannel, content);
    }

    public static void writeFromStringByNIO(FileOutputStream output, CharsetEnum charset, String content) {
        FileChannel fileChannel = output.getChannel();
        close(output);
        writeFromString(fileChannel, charset, content);
    }

    public static void writeFromStringByNIO(File outFile, String content) {
        writeFromString(fileOutputChannel(outFile, false), content);
    }

    public static void writeFromStringByNIO(File outFile, CharsetEnum charset, String content) {
        writeFromString(fileOutputChannel(outFile, false), charset, content);
    }

    public static void writeFromStringByNIO(File outFile, boolean append, String content) {
        writeFromString(fileOutputChannel(outFile, append), content);
    }

    public static void writeFromStringByNIO(File outFile, boolean append, CharsetEnum charset, String content) {
        writeFromString(fileOutputChannel(outFile, append), charset, content);
    }


    public static void writeFromStringByNIO(String outFilePath, String content) {
        writeFromString(fileOutputChannel(outFilePath, false), content);
    }

    public static void writeFromStringByNIO(String outFilePath, CharsetEnum charset, String content) {
        writeFromString(fileOutputChannel(outFilePath, false), charset, content);
    }

    public static void writeFromStringByNIO(String outFilePath, boolean append, String content) {
        writeFromString(fileOutputChannel(outFilePath, append), content);
    }

    public static void writeFromStringByNIO(String outFilePath, boolean append, CharsetEnum charset, String content) {
        writeFromString(fileOutputChannel(outFilePath, append), charset, content);
    }


    @Note("将 content 字节数组写入字节流中")
    public static void writeFromBytes(OutputStream output, byte[] content) {
        if(output == null || content == null || content.length == 0) return;
        try {
            output.write(content);
            output.flush();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(output);
        }
    }

    public static void writeFromBytes(File outFile, byte[] content) {
        writeFromBytes(fileOutputStream(outFile), content);
    }

    public static void writeFromBytes(String outFilePath, byte[] content) {
        writeFromBytes(fileOutputStream(outFilePath), content);
    }

    @Note("把字节数组用 NIO 的方式写入指定的位置")
    public static void writeFromBytes(FileChannel outputChannel, byte[] content) {
        if(outputChannel == null || content == null || content.length == 0) return;
        private_nioWriteFromString(outputChannel, content);
    }

    public static void writeFromBytesByNIO(FileOutputStream output, byte[] content) {
        FileChannel fileChannel = output.getChannel();
        close(output);
        writeFromBytes(fileChannel, content);
    }

    public static void writeFromBytesByNIO(File outFile, byte[] content) {
        writeFromBytes(fileOutputChannel(outFile, false), content);
    }

    public static void writeFromBytesByNIO(String outFilePath, byte[] content) {
        writeFromBytes(fileOutputChannel(outFilePath, false), content);
    }

    @Note("IO 字符流之间的拷贝操作")
    public static void write(CharIOCopyBean charIOCopyBean) {
        if(charIOCopyBean == null || !charIOCopyBean.enableCopy()) return;
        char[] buffs = new char[charIOCopyBean.getBufferSizeOrReplace(DEFAULT_BUFFERED_BYTE_SIZE)];
        Reader reader = charIOCopyBean.getReader();
        Writer writer = charIOCopyBean.getWriter();
        private_charStreamCopy(reader, writer, buffs);
    }



    @Note("IO 字节流之间的拷贝操作")
    public static void write(ByteIOCopyBean byteIOCopyBean) {
        if(byteIOCopyBean == null || !byteIOCopyBean.enableCopy()) return;
        byte[] buffs = new byte[byteIOCopyBean.getBufferSizeOrReplace(DEFAULT_BUFFERED_BYTE_SIZE)];
        InputStream input = byteIOCopyBean.getInput();
        OutputStream output = byteIOCopyBean.getOutput();
        private_byteStreamCopy(input, output, buffs);
    }


    @Note("使用 NIO 拷贝字节数据")
    public static void write(NIOCopyBean nioCopyBean) {
        if(nioCopyBean == null) return;
        if(!nioCopyBean.enableCopy()) return;
        FileChannel input = nioCopyBean.getInput();
        FileChannel output = nioCopyBean.getOutput();
        int bufferSize = nioCopyBean.getBufferSizeOrReplace(DEFAULT_BUFFERED_BYTE_SIZE);
        try {
            ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
            while (input.read(buffer) != -1) {
                buffer.flip();
                output.write(buffer);
                buffer.clear();
            }
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(input);
            close(output);
        }
    }


    @Note("把图片对象转为字节流，写入到字节输出流中去（imgSuffix 是图片的后缀，不要加【.】）")
    public static void writeImage(BufferedImage image, String imgSuffix, OutputStream output) {
        try {
            ImageIO.write(image, imgSuffix, output);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("图片对象转为字节流写入到对应文件位置中去（imgSuffix 是图片的后缀，不要加【.】）")
    public static void writeImage(BufferedImage sourceImage, String imgSuffix, File targetPath) {
        try {
            ImageIO.write(sourceImage, imgSuffix, targetPath);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("把图片对象转为字节流写入到对应路径中去生成对应的文件（imgSuffix 是图片的后缀，不要加【.】）")
    public static void writeImage(BufferedImage sourceImage, String imgSuffix, String targetPath) {
        try {
            File file = new File(targetPath);
            ImageIO.write(sourceImage, imgSuffix, file);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }


    @Note("将 base64 字符串转为图片字节流，然后写入到指定路径中")
    public static void writeImage(String base64, String imageSuffix, String imageOutPath) {
        writeImage(base64, imageSuffix, fileOutputStream(imageOutPath));
    }

    @Note("将 base64 字符串转为图片字节流，然后写入到指定路径中")
    public static void writeImage(String base64, String imageSuffix, File imageOutFile) {
        writeImage(base64, imageSuffix, fileOutputStream(imageOutFile));
    }

    @Note("将 base64 字符串转为图片字节流，然后写入到指定路径中")
    public static void writeImage(String base64, String imageSuffix, OutputStream imageOut) {
        base64 = base64.replace("data:image/" + imageSuffix + ";base64,", "");
        byte[] bytes = Base64.getDecoder().decode(base64);
        try {
            imageOut.write(bytes);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(imageOut);
        }
    }

    /* *******************************************************************************************
     *
     *          流传输操作
     *
     * *******************************************************************************************
     * */

    /**
     * @param byteIOCopyBean IO 字节流拷贝操作包装类
     * 将源字节输入流中的内容拷贝至字节输出流中
     * */
    @Note("将源字节输入流中的内容拷贝至字节输出流中")
    public static void copy(ByteIOCopyBean byteIOCopyBean) {
        if(byteIOCopyBean == null) return;
        if(!byteIOCopyBean.enableCopy()) return;
        InputStream input = byteIOCopyBean.getInput();
        OutputStream output = byteIOCopyBean.getOutput();
        byte[] buffs = new byte[byteIOCopyBean.getBufferSizeOrReplace(DEFAULT_BUFFERED_BYTE_SIZE)];
        private_byteStreamCopy(input, output, buffs);
    }

    /**
     * @param charIOCopyBean IO 字符流拷贝操作包装类
     * 将源字符输入流中的内容拷贝至字符输出流中
     * */
    @Note("将源字符输入流中的内容拷贝至字符输出流中")
    public static void copy(CharIOCopyBean charIOCopyBean) {
        if(charIOCopyBean == null) return;
        if(!charIOCopyBean.enableCopy()) return;
        Reader reader = charIOCopyBean.getReader();
        Writer writer = charIOCopyBean.getWriter();
        char[] buffs = new char[charIOCopyBean.getBufferSizeOrReplace(DEFAULT_BUFFERED_BYTE_SIZE)];
        private_charStreamCopy(reader, writer, buffs);
    }



    @Note("使用 NIO 进行拷贝操作")
    public static void copy(NIOCopyBean nioCopyBean) {
        if(nioCopyBean == null) return;
        if(!nioCopyBean.enableCopy()) return;
        FileChannel input = nioCopyBean.getInput();
        FileChannel output = nioCopyBean.getOutput();
        try {
            long fileSize = input.size();
            if(fileSize <= NIO_TRANS_LIMIT) {
                output.transferFrom(input, input.position(), fileSize);
                return;
            }
            long remain = fileSize % NIO_TRANS_LIMIT;
            long partCount = fileSize / NIO_TRANS_LIMIT;
            boolean flag = remain != 0;
            if(flag) {
                partCount++;
            }
            long pos = input.position();
            for (int i = 0; i < partCount; i++) {
                if((i == partCount - 1) && flag) {
                    output.transferFrom(input, pos, remain);
                    return;
                }
                output.transferFrom(input, pos, NIO_TRANS_LIMIT);
                pos += NIO_TRANS_LIMIT;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            close(input);
            close(output);
        }
    }

    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("使用 BufferedReader 来读取数据转为字符串")
    private static String private_bufferedReadOperation(BufferedReader reader, Force<String> forceHandle) {
        StringBuilder builder = new StringBuilder();
        String line;
        String lineSeparator = SysKit.getLineSeparator();
        try {
            if(forceHandle == null) {
                while ((line = reader.readLine()) != null) {
                    builder.append(line).append(lineSeparator);
                }
                return StrKit.substringLast(builder.toString(), lineSeparator);
            }
            while ((line = reader.readLine()) != null) {
                builder.append(forceHandle.force(line)).append(lineSeparator);
            }
            return StrKit.substringLast(builder.toString(), lineSeparator);
        } catch (Exception e) {
            throw new IORuntimeException(e);
        } finally {
            close(reader);
        }
    }


    @Note("使用 OutputStream 字节输出流写入字符串")
    private static void private_outPutStreamWriteString(OutputStream output, byte[] bytes) {
        if(output == null) return;
        try {
            output.write(bytes, 0, bytes.length);
            output.flush();
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(output);
        }
    }

    @Note("字节流读取的公共逻辑")
    private static void private_byteStreamCopy(InputStream input, OutputStream output, byte[] buffs) {
        int length;
        try {
            while ((length = input.read(buffs)) != -1) {
                output.write(buffs, 0 ,length);
                output.flush();
            }
        } catch (Exception e) {
            throw new IORuntimeException(e);
        } finally {
            close(input);
            close(output);
        }
    }

    @Note("字符流读取的公共逻辑")
    private static void private_charStreamCopy(Reader reader, Writer writer, char[] buffs) {
        try {
            int length;
            while ((length = reader.read(buffs)) != -1) {
                writer.write(buffs, 0 ,length);
                writer.flush();
            }
        } catch (Exception e) {
            throw new IORuntimeException(e);
        } finally {
            close(reader);
            close(writer);
        }
    }

    @Note("使用 NIO 的方式写入字符串数据的字节数组")
    private static void private_nioWriteFromString(FileChannel outputChannel, byte[] dataBytes) {
        try {
            outputChannel.write(ByteBuffer.wrap(dataBytes));
        } catch (IOException e) {
            throw new IORuntimeException(e);
        } finally {
            close(outputChannel);
        }
    }
}
